package error;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;

import processor.MyException;
import processorIO.FileIO;
import userInterface.TextOutput;

public class ClassError {

    /** The internal representation of an informational message */
    private static final int INFO = 100;
    /** The internal representation of a warning message */
    private static final int WARNING = 200;
    /** The internal representation of a severe message */
    private static final int SEVERE = 300;
    
    /** The name of the class that this ClassError is logging. */
    private final String class_name;
    
    /** A list containing all the info errors, warning errors, and severe 
     errors */
    private ArrayList<ErrorMessage> infoErrors, warningErrors, severeErrors;

    public ClassError(String class_name) {
        this.class_name = class_name;
        this.infoErrors = new ArrayList<ErrorMessage>();
        this.warningErrors = new ArrayList<ErrorMessage>();
        this.severeErrors = new ArrayList<ErrorMessage>();
    }

    private ArrayList<String> getErrorsAsString
            (ArrayList<ErrorMessage> errorList) {
        ArrayList<String> info = new ArrayList<String>();
        info.add(class_name);
        for (ErrorMessage error : errorList) {
            info.add(error.getErrorMsg());
        }

        return info;
    }

    public ArrayList<String> getInfoErrors() {
        return getErrorsAsString(infoErrors);
    }

    public ArrayList<String> getWarningErrors() {
        return getErrorsAsString(warningErrors);
    }

    public ArrayList<String> getSevereErrors() {
        return getErrorsAsString(severeErrors);
    }

    /**
     * Logs an information message.
     * 
     * @param info_msg the information message to be logged.
     */
    public void info(String info_msg) {
        info(info_msg, null);
    }

    /**
     * Logs an information message, along with an Exception that was caught.
     * 
     * @param info_msg
     * @param e 
     */
    public void info(String info_msg, Exception e) {
        infoErrors.add(new ErrorMessage(info_msg, e, INFO));
    }

    public void warning(String error_msg) {
        warning(error_msg, null);
    }

    public void warning(String error_msg, Exception e) {
        warningErrors.add(new ErrorMessage(error_msg, e, WARNING));
    }

    public void severe(String error_msg) {
        severe(error_msg, null);
    }

    public void severe(String error_msg, Exception e) {
        severeErrors.add(new ErrorMessage(error_msg, e, SEVERE));
    }

    /** Save all the errors stored in this ErrorLogger. */
    public void saveErrors(String saveDirectory) throws IOException, 
            FileNotFoundException {
        if ((saveDirectory == null) || (saveDirectory.isEmpty())) {
            saveDirectory = "Error_Logs";
        }
        saveDirectory += "/" + class_name;

        BufferedWriter 
                info = FileIO.getFileWriter(saveDirectory + "/info.txt", false), 
                warning = FileIO.getFileWriter(saveDirectory + "/warning.txt", false), 
                severe= FileIO.getFileWriter(saveDirectory + "/severe.txt", false);
        
        FileIO.write(getErrorsAsString(infoErrors), info, true);
        FileIO.write(getErrorsAsString(warningErrors), warning, true);
        FileIO.write(getErrorsAsString(severeErrors), severe, true);
    }

    /**
     * Redirects System.err stream to a text file saved to the given
     * directory/class_name_system.err_redirect_log.txt. Return true if
     * the redirecting worked, false otherwise.
     *
     * @param directory
     * @param class_name
     * @throws FileNotFoundException  if the system_err_redirect file could not be found.
     * @throws FileProcessorException if the system_err_redirect file could not be created.
     */
    public static boolean redirectErrStream(String directory, String class_name) {
        if ((directory == null) || (directory.isEmpty())) {
            directory = "Error_Logs";
        }

        File file = new File(directory + "/"
                + class_name.replace(".", "_") + "_system_err_redirect.txt");
        PrintStream printStream;
        try {
            printStream = new PrintStream(new FileOutputStream(file));
        } catch (FileNotFoundException e) {
            return false;
        }

        System.setErr(printStream);
        System.err.println(TextOutput.SEPARATOR);
        System.err.println(class_name + " Error Log");
        //System.err.println("Date: " + getDateTime());
        System.err.println(TextOutput.SEPARATOR);

        return true;
    }

    private class ErrorMessage {

        private final String error_msg;
        private String stack_trace;
        private final Exception e;
        private final int level;

        private ErrorMessage(String error_msg, int level) {
            this(error_msg, null, level);
        }

        private ErrorMessage(String error_msg, Exception e, int level) {
            this.error_msg = error_msg;
            this.e = e;
            this.level = level;
        }

        /**
         * Returns the error message of this Error and any stack
         * traces if available.
         *
         * @return
         */
        private String getErrorMsg() {
            return error_msg + '\n' + getStackTrace();
        }

        /**
         * Returns the String representing the StackTrace of this Error
         * 
         * @return
         */
        private String getStackTrace() {
            if (stack_trace == null) {
                if (e != null) {
                    stack_trace = "";
                    StackTraceElement[] stackElements = e.getStackTrace();
                    for (StackTraceElement element : stackElements) {
                        stack_trace += element.toString() + '\n';
                    }
                    stack_trace = stack_trace.trim();
                } else {
                    stack_trace = "(No stack trace available)";
                }
            }

            return stack_trace;
        }
    }
}
